﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;

namespace PingSwitch
{
    public partial class frmPingSwitch : Form
    {
        static string currentFolder = Path.GetDirectoryName(Application.ExecutablePath);
        string hostList = "hostlist.txt";
        // Contains list of host objects and is bound to the datatable
        BindingList<Host> Hosts = new BindingList<Host>();
        public bool listChanged = false;
        bool firstPing = true;

        public frmPingSwitch()
        {
            InitializeComponent();
            Hosts.AllowRemove = true;
            initializeDatagrid();
        }

        private void initializeDatagrid()
        {
            DataGridViewTextBoxColumn hostColumn = new DataGridViewTextBoxColumn();
            hostColumn.DataPropertyName = "Name";
            hostColumn.HeaderText = "Host";

            DataGridViewTextBoxColumn resultColumn = new DataGridViewTextBoxColumn();
            resultColumn.DataPropertyName = "LastStatus";
            resultColumn.HeaderText = "Result";

            dgPingList.Columns.Add(hostColumn);
            dgPingList.Columns.Add(resultColumn);

            // Bind the bindinglist to datagridview.
            dgPingList.AutoGenerateColumns = false;
            dgPingList.Columns[0].Width = 150;
            dgPingList.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            dgPingList.DataSource = Hosts;
        }

        private void frmWOH_Load(object sender, EventArgs e)
        {
            string file = currentFolder + "\\" + hostList;
            loadHostList(readHostsFile(file));
            Hosts.ListChanged += new ListChangedEventHandler(Hosts_ListChanged);
            // Raise ListChanged events when new parts are added.
            Hosts.RaiseListChangedEvents = true;
            startPing();
        }

        private void frmWOH_FormClosing(object sender, FormClosingEventArgs e)
        {
            string file = currentFolder + "\\" + hostList;
            saveHostList(file);
        }

        private void addHostToList(string strNewHost, string hint)
        {
            bool hostExists = false;

            foreach (Host host in Hosts)
            {
                if (host.Name.ToString() == strNewHost) 
                {
                    hostExists = true;
                    host.setHints(hint);
                    break;
                }
            }

            if (!hostExists)
            {
                Host newHost = new Host(strNewHost, hint);
                Hosts.Add(newHost);
            }
        }

        public void clearBindingList()
        {
            firstPing = true;
            Hosts.Clear();
        }

        private static IPAddress GetIpFromHost(ref string workstation)
        {
            //variable to hold our error message (if something fails)
            string errMessage = string.Empty;
            //IPAddress instance for holding the returned host
            IPAddress address = null;
            //wrap the attempt in a try..catch to capture
            //any exceptions that may occur
            try
            {
                //get the host IP from the name provided
                address = Dns.GetHostEntry(workstation).AddressList[0];
            }
            catch (System.Net.Sockets.SocketException ex)
            {
                //some DNS error happened, return the message
                errMessage = string.Format("DNS Error: {0}", ex.Message);
            }
            return address;
        }

        private static string PingHost(Host host)
        {
            string strHost = host.Name.ToString();
            //string to hold our return messge
            IPStatus returnMessage = IPStatus.Unknown;
            //IPAddress instance for holding the returned host
            IPAddress address = null;
            // Find out if host is allready an ip address
            if (IPAddress.TryParse(strHost, out address))
            {
                address = IPAddress.Parse(strHost);
            }
            else
            {
                address = GetIpFromHost(ref strHost);
            }

            //set the ping options, TTL 128
            PingOptions pingOptions = new PingOptions(Properties.Settings.Default.TimeToLive, true);
            //create a new ping instance
            Ping ping = new Ping();
            //32 byte buffer (create empty)
            byte[] buffer = new byte[32];
            if (address != null)
            {
                try
                {
                    //send the ping 4 times to the host and record the returned data.
                    //The Send() method expects 4 items:
                    //1) The IPAddress we are pinging
                    //2) The timeout value
                    //3) A buffer (our byte array)
                    //4) PingOptions
                    PingReply pingReply = ping.Send(address, 1000, buffer, pingOptions);
                    //make sure we dont have a null reply
                    if (!(pingReply == null))
                    {
                        returnMessage = pingReply.Status;
                    }
                    else
                        returnMessage = pingReply.Status;
                }
                catch (PingException ex)
                {
                    returnMessage = IPStatus.Unknown;
                }
                catch (System.Net.Sockets.SocketException ex)
                {
                    returnMessage = IPStatus.Unknown;
                }
            }
            else
            {
                returnMessage = IPStatus.Unknown;
            }
            //return the message
            return returnMessage.ToString();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private string[] readHostsFile(string file)
        {
            makeSureFileExists(file);
            string[] lines = null;
            try
            {
                lines = System.IO.File.ReadAllLines(file);
            }
            catch (Exception Ex)
            {
                MessageBox.Show(Ex.ToString());
            }
            return lines;
        }

        public void loadHostList(string[] hostlist)
        {
            string strippedLine = "";
            string currentHint = "";
            for (int i = 0; i < hostlist.Length; i++)
            {
                strippedLine = hostlist[i].Trim(new Char[] { ' ', '*', '.', ':', ';', ',' });
                if (strippedLine.Length > 0)
                {
                    if (strippedLine.Substring(0, 1) == "#") currentHint = strippedLine.Substring(1);
                    this.addHostToList(strippedLine, currentHint);
                }
            }
        }

        //Saves the currently monitored hosts to file.
        private void saveHostList(string hostlist)
        {
            try
            {
                using (System.IO.StreamWriter file = new System.IO.StreamWriter(hostlist))
                {
                    foreach (Host host in Hosts)
                    {
                        file.WriteLine(host.Name.ToString());
                    }
                }
            }
            catch (Exception Ex)
            {
                MessageBox.Show(Ex.ToString());
            }
        }

        private void makeSureFileExists(string file)
        {
            if (!File.Exists(file))
            {
                try
                {
                    StreamWriter sw = File.CreateText(file);
                }
                catch (Exception Ex)
                {
                    MessageBox.Show(Ex.ToString());
                }
            }
        }

        private int onlineHosts()
        {
            int count = Hosts.Where(Host => Host.LastStatus.Equals("Success")).Select(Host => Host).Count();
            return count;
        }

        private int hostCount()
        {
            int count = Hosts.Count - Hosts.Where(Host => Host.Name.StartsWith("#")).Select(Host => Host).Count();
            return count;
        }

        private void btnAdd_Click(object sender, EventArgs e)
        {
            string[] strHosts = Hosts.Select(s => s.Name).ToArray();

            frmAddComputers addComputers = new frmAddComputers(this, strHosts);
            addComputers.Show();
        }

        private void startPing()
        {
            if (!this.backgroundWorker1.IsBusy && Hosts.Count > 0)
            {
                this.backgroundWorker1.RunWorkerAsync();
            }
        }

        private void stopPing()
        {
            if (backgroundWorker1.WorkerSupportsCancellation == true)
            {
                backgroundWorker1.CancelAsync();
            }
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            if ((worker.CancellationPending == true))
            {
                e.Cancel = true;
            }
            else
            {
                string status = "Unknown";
                while(true)
                {
                    Start:
                    listChanged = false;
                    //foreach (Host host in Hosts)
                    for (int i = Hosts.Count - 1; i >= 0; i--)
                    {
                        //als de hostlijst bijgewerkt is moet de loop opnieuw beginnen.
                        if (listChanged) goto Start;

                        DataGridViewRow row = dgPingList.Rows[i];
                        Host host = Hosts[i];
                        if (host.Name.Substring(0, 1) == "#")
                        {
                            host.LastStatus = "";
                            row.DefaultCellStyle.BackColor = Color.White;
                            row.DefaultCellStyle.Font = new Font(DataGridView.DefaultFont, FontStyle.Bold);
                            continue;
                        }

                        row.DefaultCellStyle.BackColor = Color.WhiteSmoke;

                        string message = "";
                        status = PingHost(host);
                        DateTime now = DateTime.Now;

                        if (status == "Success")
                        {
                            if (Properties.Settings.Default.AlertWhenUp && host.LastStatus != "Success" && !firstPing)
                            {
                                message = host.Name + "#Is nu online (" + now.ToShortTimeString() + ")#green";
                            }
                            row.DefaultCellStyle.BackColor = Color.LightGreen;
                        }
                        else
                        {
                            if (Properties.Settings.Default.AlertWhenDown && host.LastStatus == "Success" && !firstPing)
                            {
                                message = host.Name + "#Is nu offline (" + now.ToShortTimeString() + ")#red";
                            }
                            row.DefaultCellStyle.BackColor = System.Drawing.Color.Empty;
                        }

                        host.LastStatus = status;
                        worker.ReportProgress(0, message);
                    }
                    firstPing = false;
                    System.Threading.Thread.Sleep(Properties.Settings.Default.PingInterval);
                }
            }
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            string[] words = e.UserState.ToString().Split('#');
            // words[0] = hostname
            // words[1] = message
            // words[2] = color

            if (words.Count() == 3)
            {
                if (e.UserState.ToString().Length > 0)
                {
                    frmNotify notify = new frmNotify(e.UserState.ToString());
                    notify.Show();
                }
            }
            
            StatusLabelPingCount.Text = "Hosts: " + hostCount() + " Online: " + onlineHosts();
            dgPingList.Refresh();
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {

        }

        private void hintsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Int32 selectedRowCount =
                dgPingList.Rows.GetRowCount(DataGridViewElementStates.Selected);

            if (selectedRowCount == 1)
            {
                string hostName = dgPingList.SelectedRows[0].Cells[0].Value.ToString();
                Host selectedHost;
                string hints = "";
                foreach (Host host in Hosts)
                {
                    if (host.Name == hostName)
                    {
                        selectedHost = host;
                        foreach (string hint in selectedHost.getHints())
                        {
                            hints += hint + Environment.NewLine;
                        }
                    }
                }
                MessageBox.Show(hints.Length == 0 ? "Geen hints." : hints);
            }

        }

        private void copyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // The context menu strip called this method, so we need to determine which DGV it was clicked on    
            ToolStripDropDownItem item = sender as ToolStripDropDownItem;
            if (item == null) // Error            
                return;
            ContextMenuStrip strip = item.Owner as ContextMenuStrip;
            DataGridView grid = strip.SourceControl as DataGridView;
            if (grid == null) // Control wasn't a DGV
                return;
            string selection = "";
            Int32 selectedRowCount =
                grid.Rows.GetRowCount(DataGridViewElementStates.Selected);
            if (selectedRowCount > 0)
            {
                for (int i = 0; i < selectedRowCount; i++)
                {
                    string hostName = grid.SelectedRows[i].Cells[0].Value.ToString();
                    selection += hostName + Environment.NewLine;
                }
            }

            // DataObject data = grid.GetClipboardContent();
            DataObject data = new DataObject();
            data.SetData(selection);
            if (data != null)
                Clipboard.SetDataObject(data);
        }

        private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Int32 selectedRowCount =
                dgPingList.Rows.GetRowCount(DataGridViewElementStates.Selected);

            if (selectedRowCount > 0)
            {
                List<Host> hostsToDelete = new List<Host>();
                // Create a list of Hosts to delete
                for (int i = 0; i < selectedRowCount; i++)
                {
                    string hostName = dgPingList.SelectedRows[i].Cells[0].Value.ToString();
                    foreach (Host host in Hosts)
                    {
                        if (host.Name == hostName)
                        {
                            hostsToDelete.Add(host);
                        }
                    }
                }
                // Delete selected host entrys
                foreach (Host host in hostsToDelete)
                {
                    Hosts.Remove(host);
                }

                //Hosts.ResetBindings();
            }
        }

        private void exitToolStripMenuItem_Click_1(object sender, EventArgs e)
        {
            this.Close();
        }

        private void aboutToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            frmAbout about = new frmAbout();
            about.Show();
        }

        public class Host
        {
            private string name;
            private string lastStatus;
            private List<string> hints = new List<string>();

            public Host(string hostName)
            {
                Name = hostName;
                lastStatus = "Unknown";
            }

            public Host(string hostName, string hint)
            {
                Name = hostName;
                if (hint.Length > 0 && !hints.Contains(hint)) hints.Add(hint);
                lastStatus = "Unknown";
            }

            public List<string> getHints()
            {
                return hints;
            }

            public void setHints(string hint)
            {
                if (hint.Length > 0 && !hints.Contains(hint)) hints.Add(hint);
            }

            public string Name
            {
                get { return name; }
                set { name = value;}
            }

            public string LastStatus
            {
                get
                {
                    return lastStatus;
                }
                set
                {
                    lastStatus = value;
                }
            }
        }

        private void closeNotificationsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            for (int i = Application.OpenForms.Count - 1; i >= 0; i--)
            {
                if (Application.OpenForms[i].Name == "frmNotify")
                    Application.OpenForms[i].Close();
            }
        }

        private void addHostsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string[] strHosts = Hosts.Select(s => s.Name).ToArray();
            frmAddComputers addComputers = new frmAddComputers(this, strHosts);
            addComputers.Show();
        }

        private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            frmSettings settings = new frmSettings();
            settings.Show();
        }

        void Hosts_ListChanged(object sender, ListChangedEventArgs e)
        {
            listChanged = true;
            // MessageBox.Show(e.ListChangedType.ToString());
        }
    }
    }